#include "errors.hh"
#include "tests.hh"
#include "leam.hh"

using namespace licada;
using namespace leam;

class BasicLeamTest : public TestBase, public ILeamHandler
{
protected:
    TDataVector leftRight;
    TDataVector rightLeft;
    LeamStream* left;
    LeamStream* right;
    LeamVectorInStream lIn;
    LeamVectorInStream rIn;
    LeamVectorOutStream lOut;
    LeamVectorOutStream rOut;

    TDataVector testData;
    bool ack, fin;
    TMessageId msg;
public:
    BasicLeamTest() : left( NULL ), right( NULL ){}
    virtual ~BasicLeamTest() { delete left; delete right; }
    virtual void setup() {
	lIn.setup( &rightLeft );
	rIn.setup( &leftRight );
	lOut.setup( &leftRight );
	rOut.setup( &rightLeft );
	left = new LeamStream( &lIn, &lOut, this, false );
	right = new LeamStream( &rIn, &rOut, this, false );
	ack = false;
	fin = false;
    }
    virtual void testPrecondition() {}
    virtual void doOperation() {
	TBeginMessageReturn begin = left->beginSendMessage( 3 );
	*begin.second << (uint8) 1;
	*begin.second << (uint8) 2;
	*begin.second << (uint8) 3;
	msg = begin.first;
	TEST_FALSE( left->isAck( begin.first ) );
	TEST_FALSE( left->isFin( begin.first ) );
	right->readNext();
	left->readNext();
	left->readNext();

    }
    virtual void testPostcondition() {
	TEST_EQ( 3, testData.size() );
	TEST_EQ( 1, testData[0] );
	TEST_EQ( 2, testData[1] );
	TEST_EQ( 3, testData[2] );
	TEST_TRUE( ack );
	TEST_TRUE( fin );
	TEST_TRUE( left->isAck( msg ) );
	TEST_TRUE( left->isFin( msg ) );
    }
    virtual void teardown(){
	delete left; left = NULL;
	delete right; right = NULL;
    }
    
    virtual void handleData( TMessageId, LeamDataInStream& data ) {
	testData.resize( data.getRemaining() );
	data >> testData;
    }
    
    virtual void handleError( TMessageId, const std::string&, LeamDataInStream& ){}
    //Acknowledged
    virtual void handleAck( TMessageId ) {
	ack = true;
    }
    //Finished
    virtual void handleFin( TMessageId ) {
	fin = true;
    }
    //Called when the *other* stream sends a close message.
    virtual void handleClose() {
    }
};

class ErrorLeamTest : public BasicLeamTest
{
protected:
    LeamDataInStream* inStream;
    LeamDataInStream* errorStream;

public:
    virtual void handleData( TMessageId, LeamDataInStream& data ) {
	testData.resize(3);
	testData[0] = 1;
	testData[1] = 2;
	testData[2] = 3;
	inStream = &data;
	throw LeamUserError( testData );
    }
    virtual void handleError( TMessageId, const std::string& _st, LeamDataInStream& dataStream){
	testData.resize( dataStream.getRemaining() );
	dataStream >> testData;
	errorStream = &dataStream;
    }
    
    virtual void testPostcondition() {
	//I need to ensure that the streams read their data even in the case of an error.
	TEST_EQ( 0, inStream->getRemaining() );
	TEST_EQ( 0, errorStream->getRemaining() );
	TEST_EQ( 3, testData.size() );
	TEST_EQ( 1, testData[0] );
	TEST_EQ( 2, testData[1] );
	TEST_EQ( 3, testData[2] );
	TEST_TRUE( ack );
	TEST_TRUE( left->isAck( msg ) );
	TEST_FALSE( fin );
	TEST_TRUE( left->isFin( msg ) );
    }
};


int main( int , char**  )
{
    runTest<BasicLeamTest>();
    runTest<ErrorLeamTest>();
}
